Виджеты. Stateless и Stateful
➡️Скачать презентацию. Flutter Stateful
➡️Ссылка на репозиторий с кодом этого урока
Подготовка
В папку stateful_widgets добавим новый файл с названием s2_stateful_widget.dart
Сделаем отдельные виджеты для каждого компонента карточки:
для title → TitleWidget
для subtitle → SubtitleWidget
для trailing → LikeButton
Управление состоянием из родительского виджета
Файл s2_stateful_widget.dart
import 'package:flutter/material.dart';
class TrackCard extends StatelessWidget {
const TrackCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: ListTile(
leading: Image.asset("assets/images/pro.webp"),
title: const TitleWidget(text: "Stateless"),
subtitle: const SubtitleWidget(text: "Flutter vibes"),
trailing: LikeButton(text: "Like"), // Передаем параметр для Stateful
),
);
}
}
Файл s2_stateful_widget.dart
class TitleWidget extends StatelessWidget {
final String text;
const TitleWidget({super.key, this.text = ""});
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(
fontWeight: FontWeight.bold,
),
);
}
}
Файл s2_stateful_widget.dart
class SubtitleWidget extends StatelessWidget {
final String text;
const SubtitleWidget({super.key, this.text = ""});
@override
Widget build(BuildContext context) {
return Text(text.toUpperCase());
}
}
Сделаем LikeButton как StatefulWidget, чтобы он управлял только сменой иконки
widget и state
- В данном случае
StatefulWidgetпринимает параметрtext - Чтобы получить доступ к этому параметру внутри
State, нужно обращаться к нему через объектwidget.text
Файл s2_stateful_widget.dart
class LikeButton extends StatefulWidget {
final String text;
const LikeButton({super.key, this.text = ""});
@override
State<LikeButton> createState() => _LikeButtonState();
}
class _LikeButtonState extends State<LikeButton> {
bool isFavorite = false;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: IconButton(
onPressed: () {
setState(() {
isFavorite = !isFavorite;
});
},
icon: isFavorite
? const Icon(Icons.favorite, color: Colors.red)
: const Icon(Icons.favorite_border),
),
),
Text(widget.text), // Чтобы получить доступ к свойствам виджета
],
);
}
}

Передача колбэк-функции
- Сделаем виджет
TrackCardкакStatefulWidgetи добавим ему хранение и управление состоянием счётчика лайков. - Но вызывать запрос на изменение состояния будет виджет потомок
LikeButton! - Для этого нужно в виджет
LikeButtonпередать как параметр целую функцию! - Такой подход называется
передача колбэкаилипередача обратной функции.
Файл s2_stateful_widget.dart
class TrackCard extends StatefulWidget {
const TrackCard({super.key});
@override
State<TrackCard> createState() => _TrackCardState();
}
class _TrackCardState extends State<TrackCard> {
int counter = 0; // Состояние хранение счётчика лайков
// Функция для изменения состояния
void _incCount() => setState(() {
counter++;
});
@override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: ListTile(
leading: Image.asset("assets/images/pro.webp"),
title: const TitleWidget(text: "Stateless"),
subtitle: const SubtitleWidget(text: "Flutter vibes"),
trailing: LikeButton(
text: "Like $counter", // Передаем значение состояния
incCount: _incCount, // Передаем в параметр целую функцию
),
),
);
}
}

⭕Важно! Когда передаём функцию-колбэк как параметр, пишем только название функции!
⭕Без круглых скобок в конце!
trailing: LikeButton(
text: "Like $counter",
incCount: _incCount, // Передаем в параметр целую функцию
),

Файл s2_stateful_widget.dart
class LikeButton extends StatefulWidget {
final String text;
final Function incCount;
const LikeButton({super.key, this.text = "", required this.incCount});
@override
State<LikeButton> createState() => _LikeButtonState();
}
class _LikeButtonState extends State<LikeButton> {
bool isFavorite = false;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: IconButton(
onPressed: () {
isFavorite = !isFavorite;
// Вызываем функцию-колбэк из виджета TrackCard
widget.incCount();
},
icon: isFavorite
? const Icon(Icons.favorite, color: Colors.red)
: const Icon(Icons.favorite_border),
),
),
Text(widget.text),
],
);
}
}